Skip to content

Conversation

@christian-bromann
Copy link
Member

@christian-bromann christian-bromann commented Nov 14, 2025

@pokey has been attempting to implement the Anthropic memory tool as part of a middleware. This PR approaches this differently and implements this tool as provider tool. Running the tool within and agent shows that the model can interact with the tool:

{
  messages: [
    HumanMessage {
      "id": "b107d300-b413-41b4-9047-06b5e41fc063",
      "content": "What is the weather in Tokyo?",
      "additional_kwargs": {},
      "response_metadata": {}
    },
    AIMessage {
      "id": "msg_01CEiqjq2L7vATupz85S17CS",
      "content": [
        {
          "type": "text",
          "text": "I'll check my memory first, then help you with the weather information."
        },
        {
          "type": "tool_use",
          "id": "toolu_01W4GpFJk6wiEhPvrZy5efLP",
          "name": "memory",
          "input": {
            "command": "view",
            "path": "/memories"
          }
        }
      ],
      "name": "model",
      "additional_kwargs": {
        "model": "claude-sonnet-4-5-20250929",
        "id": "msg_01CEiqjq2L7vATupz85S17CS",
        "type": "message",
        "role": "assistant",
        "stop_reason": "tool_use",
        "stop_sequence": null,
        "usage": {
          "input_tokens": 1579,
          "cache_creation_input_tokens": 0,
          "cache_read_input_tokens": 0,
          "cache_creation": {
            "ephemeral_5m_input_tokens": 0,
            "ephemeral_1h_input_tokens": 0
          },
          "output_tokens": 85,
          "service_tier": "standard"
        },
        "context_management": {
          "applied_edits": []
        }
      },
      "response_metadata": {
        "model": "claude-sonnet-4-5-20250929",
        "id": "msg_01CEiqjq2L7vATupz85S17CS",
        "stop_reason": "tool_use",
        "stop_sequence": null,
        "usage": {
          "input_tokens": 1579,
          "cache_creation_input_tokens": 0,
          "cache_read_input_tokens": 0,
          "cache_creation": {
            "ephemeral_5m_input_tokens": 0,
            "ephemeral_1h_input_tokens": 0
          },
          "output_tokens": 85,
          "service_tier": "standard"
        },
        "context_management": {
          "applied_edits": []
        },
        "type": "message",
        "role": "assistant",
        "model_provider": "anthropic"
      },
      "tool_calls": [
        {
          "name": "memory",
          "args": {
            "command": "view",
            "path": "/memories"
          },
          "id": "toolu_01W4GpFJk6wiEhPvrZy5efLP",
          "type": "tool_call"
        }
      ],
      "invalid_tool_calls": [],
      "usage_metadata": {
        "input_tokens": 1579,
        "output_tokens": 85,
        "total_tokens": 1664,
        "input_token_details": {
          "cache_creation": 0,
          "cache_read": 0
        }
      }
    },
    ToolMessage {
      "id": "c4ce0fb9-824c-4ca8-ae35-796e201d25c1",
      "content": "Directory: /memories\n- weather_in_tokyo.txt",
      "name": "memory",
      "additional_kwargs": {},
      "response_metadata": {},
      "tool_call_id": "toolu_01W4GpFJk6wiEhPvrZy5efLP"
    },
    AIMessage {
      "id": "msg_015tZU7NaGgTRvKTUEUAq8Ls",
      "content": [
        {
          "type": "text",
          "text": "Let me check what information I have about Tokyo's weather:"
        },
        {
          "type": "tool_use",
          "id": "toolu_017gEW3kSusW1SotXxEdbEFX",
          "name": "memory",
          "input": {
            "command": "view",
            "path": "/memories/weather_in_tokyo.txt"
          }
        }
      ],
      "name": "model",
      "additional_kwargs": {
        "model": "claude-sonnet-4-5-20250929",
        "id": "msg_015tZU7NaGgTRvKTUEUAq8Ls",
        "type": "message",
        "role": "assistant",
        "stop_reason": "tool_use",
        "stop_sequence": null,
        "usage": {
          "input_tokens": 1689,
          "cache_creation_input_tokens": 0,
          "cache_read_input_tokens": 0,
          "cache_creation": {
            "ephemeral_5m_input_tokens": 0,
            "ephemeral_1h_input_tokens": 0
          },
          "output_tokens": 90,
          "service_tier": "standard"
        },
        "context_management": {
          "applied_edits": []
        }
      },
      "response_metadata": {
        "model": "claude-sonnet-4-5-20250929",
        "id": "msg_015tZU7NaGgTRvKTUEUAq8Ls",
        "stop_reason": "tool_use",
        "stop_sequence": null,
        "usage": {
          "input_tokens": 1689,
          "cache_creation_input_tokens": 0,
          "cache_read_input_tokens": 0,
          "cache_creation": {
            "ephemeral_5m_input_tokens": 0,
            "ephemeral_1h_input_tokens": 0
          },
          "output_tokens": 90,
          "service_tier": "standard"
        },
        "context_management": {
          "applied_edits": []
        },
        "type": "message",
        "role": "assistant",
        "model_provider": "anthropic"
      },
      "tool_calls": [
        {
          "name": "memory",
          "args": {
            "command": "view",
            "path": "/memories/weather_in_tokyo.txt"
          },
          "id": "toolu_017gEW3kSusW1SotXxEdbEFX",
          "type": "tool_call"
        }
      ],
      "invalid_tool_calls": [],
      "usage_metadata": {
        "input_tokens": 1689,
        "output_tokens": 90,
        "total_tokens": 1779,
        "input_token_details": {
          "cache_creation": 0,
          "cache_read": 0
        }
      }
    },
    ToolMessage {
      "id": "a477ec43-f8e8-4e8a-b5d0-9b2ab25e0384",
      "content": "1|{\"weather\":\"sunny\",\"temperature\":20,\"humidity\":50,\"wind_speed\":10,\"wind_direction\":\"N\",\"wind_gust\":15,\"wind_gust_direction\":\"N\",\"wind_gust_speed\":20,\"time\":\"2025-11-14T07:41:48.543Z\"}",
      "name": "memory",
      "additional_kwargs": {},
      "response_metadata": {},
      "tool_call_id": "toolu_017gEW3kSusW1SotXxEdbEFX"
    },
    AIMessage {
      "id": "msg_01UNpzE3YXH44wfWkNTkRtfH",
      "content": "I apologize, but I don't have access to real-time weather data or weather APIs. The information in my memory appears to be from a previous interaction and is not current.\n\nTo get accurate, up-to-date weather information for Tokyo, I recommend:\n\n1. **Weather websites**: Check weather.com, accuweather.com, or weather.gov\n2. **Search engines**: Simply search \"Tokyo weather\" on Google or Bing\n3. **Weather apps**: Use apps like Weather Channel, AccuWeather, or your phone's built-in weather app\n4. **Japan Meteorological Agency**: Visit their official website at jma.go.jp for official forecasts\n\nThese sources will provide you with current conditions, temperature, humidity, precipitation, and forecasts for Tokyo.",
      "name": "model",
      "additional_kwargs": {
        "model": "claude-sonnet-4-5-20250929",
        "id": "msg_01UNpzE3YXH44wfWkNTkRtfH",
        "type": "message",
        "role": "assistant",
        "stop_reason": "end_turn",
        "stop_sequence": null,
        "usage": {
          "input_tokens": 1861,
          "cache_creation_input_tokens": 0,
          "cache_read_input_tokens": 0,
          "cache_creation": {
            "ephemeral_5m_input_tokens": 0,
            "ephemeral_1h_input_tokens": 0
          },
          "output_tokens": 174,
          "service_tier": "standard"
        },
        "context_management": {
          "applied_edits": []
        }
      },
      "response_metadata": {
        "model": "claude-sonnet-4-5-20250929",
        "id": "msg_01UNpzE3YXH44wfWkNTkRtfH",
        "stop_reason": "end_turn",
        "stop_sequence": null,
        "usage": {
          "input_tokens": 1861,
          "cache_creation_input_tokens": 0,
          "cache_read_input_tokens": 0,
          "cache_creation": {
            "ephemeral_5m_input_tokens": 0,
            "ephemeral_1h_input_tokens": 0
          },
          "output_tokens": 174,
          "service_tier": "standard"
        },
        "context_management": {
          "applied_edits": []
        },
        "type": "message",
        "role": "assistant",
        "model_provider": "anthropic"
      },
      "tool_calls": [],
      "invalid_tool_calls": [],
      "usage_metadata": {
        "input_tokens": 1861,
        "output_tokens": 174,
        "total_tokens": 2035,
        "input_token_details": {
          "cache_creation": 0,
          "cache_read": 0
        }
      }
    }
  ]
}

I don't think there is any additional value in implementing this as middleware. What I like about this approach is that:

  • the raw memory tool is still barebone and allows the user to interact with the provider as close as possible
    const agent = createAgent({
      model,
      tools: [tools.memory_20250818({
        execute: async (args) => {
          // implement me
          return "Not implemented";
        },
      })],
    });
    This allows us to update the raw provider interfaces and follow Anthropic's support lifecycle
  • we implement an "opinionated" tool that comes with an abstraction for files - it uses the "latest" memory features
  • no Node.js primitives are used - all pure JS

@changeset-bot
Copy link

changeset-bot bot commented Nov 14, 2025

⚠️ No Changeset found

Latest commit: 8ab0863

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pokey
Copy link
Contributor

pokey commented Nov 14, 2025

Nice! I guess what I'm not following here is how the files end up on the graph state. In your example, it looks like the files just end up in an object that's a global variable declared in the example file. Is there some magic I'm missing here to propagate this to / from the graph state?

@christian-bromann
Copy link
Member Author

Is there some magic I'm missing here to propagate this to / from the graph state?

If your goal is it to store the file system in the graph as part of your agent you could do this via:

import { z } from "zod";
import { ChatAnthropic, tools, StateFileSystem } from "@langchain/anthropic";

import { createAgent, createMiddleware } from "langchain";

const model = new ChatAnthropic({
  model: "claude-sonnet-4-5-20250929",
  temperature: 0,
  clientOptions: {
    defaultHeaders: {
      "anthropic-beta": "context-management-2025-06-27",
    },
  },
});

const memoryMiddleware = () => {
  const files = {};
  const filesystem = new StateFileSystem(files, [], (files) => {
    Object.assign(files, files);
  });

  return createMiddleware({
    name: "MemoryMiddleware",
    stateSchema: z.object({
      files: z.record(
        z.string(),
        z.array(
          z.object({
            content: z.string(),
            createdAt: z.string(),
          })
        )
      ),
    }),
    tools: [tools.memory({ filesystem })],
    beforeAgent: async (state) => {
      // update the filesystem with files within the state
      await Promise.all(
        Object.entries(state.files).map(async ([filepath, fileData]) =>
          filesystem.writeFile(filepath, fileData)
        )
      );
    },
  });
};

const agent = createAgent({
  model,
  middleware: [memoryMiddleware],
});

const result = await agent.invoke({
  messages: "What is the weather in Tokyo?",
});

console.log(result);

@pokey
Copy link
Contributor

pokey commented Nov 15, 2025

Hmm still not sure I'm following. I see how the filesystem gets what's initially on the graph, but I'm still not seeing how it updates the graph state after the tool call

@christian-bromann
Copy link
Member Author

It's part of the tool implementation in the provider package. By passing in a FileSystem type (either memory of fs style filesystem) it will be attached to the tool call and handled as part of the tool implementation.

@pokey
Copy link
Contributor

pokey commented Nov 16, 2025

Maybe worth an integration test or two to show how that end-to-end pipeline works? Could just use / adapt mine

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants